package es

import (
	"context"
	"encoding/json"
	"fmt"
	es "github.com/olivere/elastic/v7"
)

/**
es  地理位置
*/

const (
	CityIndex = "city-go"
)

//城市
type City struct {
	ID       string    `json:"id"`
	CityName string    `json:"city_name"` //市
	Province string    `json:"province"`  //省
	Location []float64 `json:"location"`  //位置  经纬度表示
}

type Point struct {
	Lat float64
	Lon float64
}

type Rectangle struct {
	TopLeft     Point
	RightBottom Point
}

func ToMap(city *City) map[string]interface{} {
	m := make(map[string]interface{})
	m["id"] = city.ID
	m["province"] = city.Province
	m["city_name"] = city.CityName
	m["location"] = map[string]float64{
		"lat": city.Location[0],
		"lon": city.Location[1],
	}

	return m
}

func FromMap(m map[string]interface{}) *City {
	var city City
	city.ID = m["id"].(string)
	city.Province = m["province"].(string)
	city.CityName = m["city_name"].(string)

	location := []float64{0, 0}
	l := m["location"].(map[string]interface{})
	location[0] = l["lat"].(float64)
	location[1] = l["lon"].(float64)

	city.Location = location
	return &city
}

func IndexCity(city *City) {
	InitClient()

	m := ToMap(city)
	resp, err := cli.Index().Index(CityIndex).Id(city.ID).
		BodyJson(m).Do(context.Background())
	CheckErr(err)
	fmt.Println(resp.Result)
}

func IndexCityBatch(citys []*City) {
	InitClient()

	bulkService := cli.Bulk().Index(CityIndex)
	for _, city := range citys {
		m := ToMap(city)
		bulkService.Add(es.NewBulkIndexRequest().Id(city.ID).Doc(m))
	}

	resp, err := bulkService.Do(context.Background())
	CheckErr(err)

	fmt.Println(resp.Errors)
}

func DeleteCity(id string) {
	InitClient()
	resp, err := cli.Delete().Index(CityIndex).Id(id).Do(context.Background())
	CheckErr(err)
	fmt.Println(resp.Result)
}

func DeleteCityBatch(ids ...string) {
	InitClient()
	bulkService := cli.Bulk().Index(CityIndex)

	for _, id := range ids {
		bulkService.Add(es.NewBulkDeleteRequest().Id(id))
	}

	resp, err := bulkService.Do(context.Background())
	CheckErr(err)
	fmt.Println(resp.Errors)
}

func getCityResult(resp *es.SearchResult) []*City {
	searchSize := len(resp.Hits.Hits)
	citys := make([]*City, searchSize, searchSize)
	if searchSize > 0 {
		for i, hit := range resp.Hits.Hits {
			var c *City
			var m = make(map[string]interface{})
			json.Unmarshal(hit.Source, &m)
			c = FromMap(m)
			citys[i] = c
		}
	}
	return citys
}

func Search(province string, origin *Point, r *Rectangle) []*City {
	InitClient()

	query := es.NewBoolQuery().
		Must(es.NewTermQuery("province", province)).
		Filter(es.NewGeoBoundingBoxQuery("location").
			TopLeft(r.TopLeft.Lat, r.TopLeft.Lon).
			BottomRight(r.RightBottom.Lat, r.RightBottom.Lon))

	searchService := cli.Search(CityIndex).
		Query(query).
		SortBy(es.NewGeoDistanceSort("location").Point(origin.Lat, origin.Lon).Asc())

	resp, err := searchService.Do(context.Background())
	CheckErr(err)
	return getCityResult(resp)
}

func SearchPoint(origin *Point, dist string) []*City {
	InitClient()

	query := es.NewGeoDistanceQuery("location").
		Point(origin.Lat, origin.Lon).
		Distance(dist)

	resp, err := cli.Search(CityIndex).
		Query(query).
		SortBy(es.NewGeoDistanceSort("location").Point(origin.Lat, origin.Lon).Asc()).
		Do(context.Background())

	CheckErr(err)
	return getCityResult(resp)
}
